/*
 * opensocial-jaxer-client 0.1.0
 * http://code.google.com/p/opensocial-jaxer-client/
 *
 * Copyright(C) 2009 LEARNING RESOURCE LAB
 * http://friendfeed.com/nakajiman
 * http://hp.submit.ne.jp/d/16750/
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations under the License.
 */
/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations under the License.
 */
var osapi = osapi || {};
osapi.base = osapi.base  || {};

osapi.base.defaultAppId = '@app';

/**
 * Make sure that the appId param is set, using the default if none is supplied.
 *
 * @param options the parameters to a jsonrpc call.
 */
osapi.base.ensureAppId = function(options) {
  options.appId = options.appId || osapi.base.defaultAppId;
};


/**
 * Set an error property on the responseMap object
 * @param responseMap results returned to a gadget
 * @param error the particular error object to set
 */
osapi.base.setGlobalError = function(responseMap, error) {
  if (!responseMap['error']) {
    if (error === undefined) {
      responseMap['error'] = "Some requests in the batch had errors";
    } else {
      responseMap['error'] = error;
    }
  }
};

/**
 * Turns an http error code into an opensocial error
 * @param httpError
 */
osapi.base.translateHttpError = function(httpError) {
  if (httpError == "Error 501") {
    return 'notImplemented';
  } else if (httpError == "Error 401") {
    return 'unauthorized';
  } else if (httpError == "Error 403") {
    return 'forbidden';
  } else if (httpError == "Error 400") {
    return 'badRequest';
  } else if (httpError == "Error 500") {
    return 'internalError';
  } else if (httpError == "Error 404") {
    return 'badRequest';
  } else if (httpError == "Error 417") {
    return 'limitExceeded';
  }
};




/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations under the License.
 */
/*
<!--
Required config:

rpcUrl: The url of the opensocial data endpoint.

-->
*/
var osapi = osapi || {};

/**
 * The bare mechanism for creating json opensocial requests.
 * The various known services, activities, appdata, people, and batch, are
 * wrappers over this call.
 */
(function() {

  var config = {};

  /**
   * @param {Object} configuration Configuration settings
   * @private
   */
  function init (configuration) {
    //config = configuration["osapi.base"];
    config = configuration;
  }

  //var requiredConfig = {
  //  rpcUrl : gadgets.config.NonEmptyStringValidator
  //};

  //gadgets.config.register("osapi.base", requiredConfig, init);

  var JSON_CONTENT_TYPE = "application/json";
  var JSON_REQUEST_CONTENT_TYPE = "JSON";
  
  //var requestBody = function(json) {
  //  return {
  //    "CONTENT_TYPE" : JSON_REQUEST_CONTENT_TYPE,
  //    "METHOD" : "POST",
  //    "AUTHORIZATION" : "SIGNED",
  //    "POST_DATA" : gadgets.json.stringify(json)
  //  };
  //};

  var url = function() {
    if (config.rpcUrl) {
      var baseUrl = config.rpcUrl.replace("%host%", document.location.host);
    }
    var url = [baseUrl, "/rpc"];
    //var token = shindig.auth.getSecurityToken();
    var token = config.fcauth;
    if (token) {
      //url.push("?st=", encodeURIComponent(token));
      url.push("?fcauth=", encodeURIComponent(token));
    }

    return url.join('');
  };

  var generateErrorResponse = function(result) {
    var globalErrorCode = osapi.base.translateHttpError(result.errors[0]
        || result.data.error) || 'internalError';

    // Populate each response(request?) item with the GlobalError.
    var errorResponseMap = {};
    osapi.base.setGlobalError(errorResponseMap, { code : globalErrorCode});
//    for (var i = 0; i < result.data.length; i++) {
//      errorResponseMap[result.data[i].id] = { error : globalErrorCode };
//    }
    return errorResponseMap;
  };

  var makeResponseProcessor = function(resultProcessor/*, userCallback*/) {
    return function(result) {
      if (result.errors[0]) {
        //userCallback(generateErrorResponse(result));
        return generateErrorResponse(result);
      } else {
        //userCallback(resultProcessor(result));
        return resultProcessor(result);
      }
    };
  };

  var defaultUserId = '@viewer';
  var defaultGroupId = '@self';
  var defaultCount = 20;

  var getDataFromResult = function(result) {
    var jsonArray = result.data;
    if (jsonArray[0].error) {
      return { error : { code : osapi.base.translateHttpError("Error "+ jsonArray[0].error.code),
        message : jsonArray[0].error.message}};
    } else if (jsonArray[0].data.list) {
      return jsonArray[0].data.list;
    } else {
      return jsonArray[0].data;
    }
  };

  var renamePropertyInObject = function(obj, originalName, newName, defaultValue) {
    if (obj[originalName]) {
      obj[newName] = obj[originalName];
      delete obj[originalName];
    } else if (defaultValue) {
      obj[newName] = defaultValue;
    }
  };

  var ensureUserId = function(options) {
    if (options.userId) {
      options.userId = (options.userId.constructor === Array) ? 
          options.userId : [options.userId];
    } else {
      options.userId = [defaultUserId];
    }
  };

  var ensureGroupId = function(options) {
    options.groupId = options.groupId || defaultGroupId;
  };

  var makeJsonRequest = function(method, options) {
    ensureUserId(options);
    ensureGroupId(options);
    var jsonParams = { method : method};
    jsonParams['params'] = options;
    return [jsonParams];
  };

  /**
   * Generates the execute method for the batch or specific json rpc request
   * @returns {Function} returns a function that takes a userCallback function
   *              where results will be returned from the call.
   */
  var makeExecuteFn = function(jsonGenerator, responseProcessor) {
    return function(userCallback) {
      userCallback = userCallback || function() {};
      var json = jsonGenerator();
      if (json.length == 0) {
        //window.setTimeout(function() {
        //    makeResponseProcessor(responseProcessor, userCallback)({errors : []});
        //  },
        //  0);
        return makeResponseProcessor(responseProcessor)({errors : []});
      } else {
        //gadgets.io.makeNonProxiedRequest(url(),
        //  makeResponseProcessor(responseProcessor, userCallback),
        //  requestBody(json),
        //  JSON_CONTENT_TYPE);
        var response = { errors: [] };
        var web = Jaxer.Web.post(url(), JSON.stringify(json), {
           as: 'text', contentType: JSON_CONTENT_TYPE, extendedResponse: true
        });
        if (web.status == 200)
          try {
            response.data = JSON.parse(web.text)
          } catch (e) {
            response.errors.push('failed to parse JSON');
          }
        else
          response.errors.push('Error ' + web.status);
        return makeResponseProcessor(responseProcessor)(response);
      }
    };
  };

  /**
  * Call to execute a jsonrpc request.
  * @param {Function} jsonGenerator the function to produce the specific request's json params
  * @param {object.<JSON>} The JSON object of parameters for the specific request
  */
  var singleRequest = function(jsonRpcServiceMethod, options) {
    options = options || {};
    var jsonGenerator = function() { return makeJsonRequest(jsonRpcServiceMethod, options); };
    var execute = makeExecuteFn(jsonGenerator, getDataFromResult);

    return {
      json : jsonGenerator,
      execute : execute
    };
  };

  /**
  * Call to execute a jsonrpc request.
  * @param {Function} jsonGenerator the function to produce the specific request's json params
  * @param {Function} resultDataProcessor the function to process the results for the
  *               particular json request.
  */
  var batchRequest = function(jsonGenerator, resultDataProcessor) {
    var execute = makeExecuteFn(jsonGenerator, resultDataProcessor);

    return {
      execute : execute
    };
  };

  osapi.init = init;
  osapi.newJsonRequest = singleRequest;
  osapi.newBatchJsonRequest = batchRequest;
})();
/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations under the License.
 */

var osapi = osapi || {};

/**
 * It is common to batch requests together to make them more efficient.
 *
 * Currently, since makeRequest is proxied, these requests are handled separately to
 * create more parallelism.
 */
osapi.newBatch = function() {

  /*
   * Latch that waits for the batch of json requests and each makeRequest call to finish before
   * calling the user callback with the responseMap.
   * Note, this makes the makeRequest calls happen in parallel, but changes the order of those calls
   * luckily everything in a batch is keyed instead of requiring ordering.
   */
  var newCountdownLatch = function(/*count, userCallback*/) {
    var responseMap = {};

    //var leftTodo = function() {
    //  var done = false;
    //  return {
    //    down : function() {
    //      if (!done) {
    //        count--;
    //        if (count === 0) {
    //          done = true;
    //          userCallback(responseMap);
    //        }
    //      }
    //    }
    //  };
    //}();

    var finishNonProxiedRequest = function(results) {
      for (var result in results) if (results.hasOwnProperty(result)) {
        responseMap[result] = results[result];
        // TODO does this copy errors over as well.
      }
      //leftTodo.down();
      return responseMap;
    };

    //var finishProxiedRequest = function(key, result, error) {
    //  responseMap[key] = result;
    //  if (error) {
    //    osapi.base.setGlobalError(responseMap, error.error);
    //  }
    //  leftTodo.down();
    //};

    //return {
    //  finishedJsonRequest : finishNonProxiedRequest
    //  finishProxiedRequest : finishProxiedRequest
    //};
    return finishNonProxiedRequest;
  };


  var callbackAsyncAndEmpty = function(/*userCallback*/) {
    //window.setTimeout(function() {
    //    userCallback({ data : {}});
    //  },
    //  0);
    return { data: {} };
  };

  //var each = function(arr, fn) {
  //  for (var i=0;i<arr.length;i++) {
  //    fn.apply(null, [arr[i]]);
  //  }
  //};

  var executeJsonRequests = function(json, getDataFromResult, countDownLatch) {
    //osapi.newBatchJsonRequest(json, getDataFromResult).execute(function(results) {
    //  countDownLatch.finishedJsonRequest(results);
    //});
    return countDownLatch(
      osapi.newBatchJsonRequest(json, getDataFromResult).execute()
    );
  };

  //var executeProxiedRequests = function(proxiedRequests, countDownLatch) {
  //  if (proxiedRequests.length > 0) {
  //    var makeRequestExecutor = function(keyRequestPair) {
  //      keyRequestPair.request.execute(function(makeRequestResult) {
  //        if (makeRequestResult.error) {
  //          countDownLatch.finishProxiedRequest(keyRequestPair.key, {
  //            error : { code : osapi.base.translateHttpError("Error " +
  //                                                            makeRequestResult.error['code']),
  //                message :makeRequestResult.error.message }},
  //          true);
  //        } else {
  //          if (makeRequestResult.data) {
  //            countDownLatch.finishProxiedRequest(keyRequestPair.key, makeRequestResult.data);
  //          }
  //        }
  //      });
  //    };
  //    each(proxiedRequests, makeRequestExecutor);
  //  }
  //};

  return function() {
    var that = {};
    
    var jsonRequests = [];
    //var proxiedRequests = [];

    /**
     * Create a new request in the batch
     * @param {string} key id for the request
     * @param {object} request the opensocial request object
     */
    var add = function(key, request) {
      //if (request.isMakeRequest) {
      //  proxiedRequests.push({key : key, request : request});
      //} else {
        jsonRequests.push({key : key, request : request});
      //}
      return that;
    };

    var json = function() {
      var jsonParams = [];
      for (var i = 0; i < jsonRequests.length; i++) {
        var request = jsonRequests[i];
        var requestJson = request.request.json()[0]; // single requests make a json array by default
        requestJson.id = request.key;
        jsonParams.push(requestJson);
      }
      return jsonParams;
    };

    var getDataFromResult = function(result) {      
      var responseMap = {};

      var data = result.data; // the json array
      for (var k = 0; k < jsonRequests.length; k++) {
        var response = data[k];
        if (response.error) {
          var error = { error : { code : osapi.base.translateHttpError("Error "
              + response.error['code']),
            message : response.error.message }};
          responseMap[response.id] = error;
          osapi.base.setGlobalError(responseMap, error.error);
        } else {
          if (response.id !== jsonRequests[k].key) {
            throw "Response Id doesn't match request key";
          } else {
            if (response.data.list) { // array result
              responseMap[response.id] = response.data.list;
            } else {  //single result
              responseMap[response.id] = response.data;
            }
          }
        }
      }
      return responseMap;
    };

    var executeRequests = function(/*userCallback*/) {
      //var count =  ((jsonRequests.length > 0) ? 1 : 0 ) + proxiedRequests.length;
      var countDownLatch = newCountdownLatch(/*count, userCallback*/);
      //executeJsonRequests(json, getDataFromResult, countDownLatch);
      //executeProxiedRequests(proxiedRequests, countDownLatch);
      return executeJsonRequests(json, getDataFromResult, countDownLatch);
    };

    /**
     * Call to make a batch execute its requests.
     * @param {Function} userCallback the callback to the gadget where results are passed.
     */
    var execute = function(/*userCallback*/) {
      if (jsonRequests.length == 0/* && proxiedRequests.length == 0*/) {
        //callbackAsyncAndEmpty(userCallback);
        return callbackAsyncAndEmpty();
      } else {                            
        //executeRequests(userCallback);
        return executeRequests();
      }
    };

    that.execute = execute;
    that.add = add;
    return that;
  };
}();

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations under the License.
 */

var osapi = osapi || {};

/**
 * Service to retrieve People via JSON RPC opensocial calls.
 */
osapi.people = function() {

  var defaultFields = ['id', 'displayName'];

  var ensureFields = function(options) {
    if (!options.fields) {
      options.fields = defaultFields;
    }
  };

  /**
  * Function to get People.
  * Options specifies parameters to the call as outlined in the
  * JSON RPC Opensocial Spec
  * http://www.opensocial.org/Technical-Resources/opensocial-spec-v081/rpc-protocol
  * @param {object.<JSON>} The JSON object of parameters for the specific request
  */
  var get = function(options) {    
    ensureFields(options);
    return osapi.newJsonRequest("people.get", options);
  };

  return {
    get: get,

    /**
    * Function to get Viewer profile.
    * Options specifies parameters to the call as outlined in the
    * JSON RPC Opensocial Spec
    * http://www.opensocial.org/Technical-Resources/opensocial-spec-v081/rpc-protocol
    * @param {object.<JSON>} The JSON object of parameters for the specific request
    */
    getViewer : function(options) {
      options = options || {};
      options.userId = "@viewer";
      options.groupId = "@self";
      return get(options);
    },

    /**
    * Function to get Viewer's friends'  profiles.
    * Options specifies parameters to the call as outlined in the
    * JSON RPC Opensocial Spec
    * http://www.opensocial.org/Technical-Resources/opensocial-spec-v081/rpc-protocol
    * @param {object.<JSON>} The JSON object of parameters for the specific request
    */
    getViewerFriends : function(options) {
      options = options || {};
      options.userId = "@viewer";
      options.groupId = "@friends";
      return get(options);
    },

    /**
    * Function to get Owner profile.
    * Options specifies parameters to the call as outlined in the
    * JSON RPC Opensocial Spec
    * http://www.opensocial.org/Technical-Resources/opensocial-spec-v081/rpc-protocol
    * @param {object.<JSON>} The JSON object of parameters for the specific request
    */
    getOwner : function(options) {
      options = options || {};
      options.userId = "@owner";
      options.groupId = "@self";
      return get(options);
    },

    /**
    * Function to get Owner's friends' profiles.
    * Options specifies parameters to the call as outlined in the
    * JSON RPC Opensocial Spec
    * http://www.opensocial.org/Technical-Resources/opensocial-spec-v081/rpc-protocol
    * @param {object.<JSON>} The JSON object of parameters for the specific request
    */
    getOwnerFriends : function(options) {
      options = options || {};
      options.userId = "@owner";
      options.groupId = "@friends";
      return get(options);
    }

  };
}();
/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations under the License.
 */

var osapi = osapi || {};

/**
 * The appdata aservice allows storage, retrieval, and deletion of
 * small amounts of data per person used by gadgets.
 */
osapi.appdata = function() {


  var ensureFieldsFromKeys = function(options) {
    if (!options.keys) {
      options.fields = [];
    } else if (options.keys.length === 0 || options.keys[0] === '*') {
      options.fields = [];
      delete options.keys;
    } else {
      options.fields = options.keys;
      delete options['keys'];
    }
  };

  var ensureData = function(options) {
    if (!options.data) {
      options.data = {};
    }
  };

  /**
  * Function to get Appdata.
  * Options specifies parameters to the call as outlined in the
  * JSON RPC Opensocial Spec
  * http://www.opensocial.org/Technical-Resources/opensocial-spec-v081/rpc-protocol
  * @param {object.<JSON>} The JSON object of parameters for the specific request
  */
  var get = function(options) {
    osapi.base.ensureAppId(options);
    ensureFieldsFromKeys(options);
    return osapi.newJsonRequest("appdata.get", options);
  };

  /**
  * Function to create or update Appdata.
  * Options specifies parameters to the call as outlined in the
  * JSON RPC Opensocial Spec
  * http://www.opensocial.org/Technical-Resources/opensocial-spec-v081/rpc-protocol
  * @param {object.<JSON>} The JSON object of parameters for the specific request
  */

  var update = function(options) {
    osapi.base.ensureAppId(options);
    ensureData(options);
    ensureFieldsFromKeys(options);
    return osapi.newJsonRequest("appdata.update", options);
  };

  /**
  * Function to delete Appdata.
  * Options specifies parameters to the call as outlined in the
  * JSON RPC Opensocial Spec
  * http://www.opensocial.org/Technical-Resources/opensocial-spec-v081/rpc-protocol
  * @param {object.<JSON>} The JSON object of parameters for the specific request
  */
  var deleteData = function(options) {
    osapi.base.ensureAppId(options);
    ensureFieldsFromKeys(options);
    return osapi.newJsonRequest("appdata.delete", options);
  };

  return {
    get: get,
    update : update,
    deleteData : deleteData
  };
}();
/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations under the License.
 */

var osapi = osapi || {};

/**
 * Activity service allows retrieval and creation of Activities
 * for opensocial People.
 */
osapi.activities = function() {

  /**
   * Function to get Activities.
   * Options specifies parameters to the call as outlined in the
   * JSON RPC Opensocial Spec
   * http://www.opensocial.org/Technical-Resources/opensocial-spec-v081/rpc-protocol
   * @param {object.<JSON>} The JSON object of parameters for the specific request
   */
  var get = function(options) {
    options = options || {};
    osapi.base.ensureAppId(options);
    return osapi.newJsonRequest("activities.get", options);
  };

   /**
   * Function to create Activities.
   * Options specifies parameters to the call as outlined in the
   * JSON RPC Opensocial Spec
   * http://www.opensocial.org/Technical-Resources/opensocial-spec-v081/rpc-protocol
   * @param {object.<JSON>} The JSON object of parameters for the specific request
   */
  var create = function(options) {
    osapi.base.ensureAppId(options);
    // TODO, do expansion of template options from userPrefs if necessary
    return osapi.newJsonRequest("activities.create", options);
  };

  return {
    get: get,
    create : create
  };
}();
